package com.chunuo.configuration;

import com.chunuo.configuration.annotation.Configuration;
import com.chunuo.configuration.annotation.ExceptionHandler;
import com.chunuo.configuration.constant.AnnotationConstant;
import com.chunuo.configuration.exception.ChunuoException;
import com.chunuo.configuration.factory.PropertiesFile;
import com.chunuo.configuration.starter.AutoConfiguration;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 类的作用
 *
 * @author liujiamiao
 * @email 2544296058@qq.com
 * @date 2020-12-23 21:50
 */
@WebServlet(urlPatterns = "/*")
public class Application extends HttpServlet {


    public static void main(String[] args) {
        Annotation[] declaredAnnotations = Application.class.getDeclaredAnnotations();
        System.out.println(Arrays.toString(declaredAnnotations));
    }

    //启动自动配置
    private AutoConfiguration autoConfiguration = AutoConfiguration.newInstance();

    //项目名
    private String projectName = String.valueOf(PropertiesFile.newInstance().get("packageName"));

    public Application()  {
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

        HttpServletRequest  request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        request.setCharacterEncoding("UTF-8");// charset
        response.setCharacterEncoding("utf-8");
        //项目名/xxxx/xxx  访问地址
        String uri = request.getRequestURI();
        System.out.println("=================== 新请求 ====================");
        System.out.println("uri = "+uri);
        System.out.println("项目名= "+projectName);
        uri = uri.substring(projectName.length());
        System.out.println("请求地址 = "+uri);
        //请求方式
        String method = request.getMethod();
        System.out.println("请求方式 = "+method);
        //四种常用请求方式由我们处理
        if (method.equals("GET")||method.equals("POST") || method.equals("PUT") || method.equals("DELETE")) {
            //得到请求参数
            Enumeration<String> parameterNames = request.getParameterNames();
            //存储参数
            Map<String,Object> argsMap = new HashMap<>();
            argsMap.put("javax.servlet.http.HttpServletRequest",request);
            argsMap.put("javax.servlet.http.HttpServletResponse",response);
            //获取参数
            while(parameterNames.hasMoreElements()){
                String name = parameterNames.nextElement();
                String parameter = request.getParameter(name);
                argsMap.put(name,parameter);
            }
            Object result = null;
            try {
                //请求体
                String param = null;
                if(!method.equals("GET")){
                    param = getRequestPostStr(request);
                    System.out.println("请求体 = "+param);
                }

                //调用我们RequestMapper方法
                result = autoConfiguration.service(uri, method, argsMap,param);

            } catch (Exception e) {
                Object controllerAdvice = autoConfiguration.component.getObjectMap().get(AnnotationConstant.CONTROLLERADVICE);
                if(controllerAdvice==null){
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }else{
                    //统一异常处理
                    //拿到异常类的全限定类名
                    String clazzName = e.toString();
                    int index = clazzName.indexOf(":");
                    if(index>0){
                        clazzName = clazzName.substring(0, index);
                    }
                    System.out.println("异常类名称 = "+clazzName);
                    //创建class
                    Class clazz = null;
                    try {
                        clazz = Class.forName(clazzName);
                    } catch (ClassNotFoundException ex) {
                        ex.printStackTrace();
                    }

                    //遍历统一异常类的所有方法
                    Method[] declaredMethods = controllerAdvice.getClass().getDeclaredMethods();
                    Method tempMethod = null;
                    int min = 999;
                    for(Method errorMethod : declaredMethods){
                        ExceptionHandler annotation = errorMethod.getAnnotation(ExceptionHandler.class);
                        //如果存在这个注解，则该异常由它处理
                        if(annotation!=null){
                            //有这个注解才处理
                            //拿到注解中的class
                            Class handle = annotation.value();
                            if(clazz==handle){
                                //如果相等的话，异常由它处理
                                tempMethod = errorMethod;
                                break;
                            }else{
                                int tempMin = 0;
                                //不一样的话,递归向上找，可以给上级异常类处理这个异常
                                Class superclass = clazz.getSuperclass();
                                //只要父类不是Throwable，则一直找
                                while(superclass!=Throwable.class){
                                    tempMin++;
                                    //父类找到与注解上标志的异常一致的话，则把该方法存起来，直到拿到最接近的异常方法
                                    if(superclass==handle){
                                        if(tempMin<min){
                                            min = tempMin;
                                            tempMethod = errorMethod;
                                        }

                                    }
                                    superclass = superclass.getSuperclass();
                                }
                            }

                        }
                    }
                    //如果没有找到合适的处理类，则抛出去
                    if(tempMethod!=null){
                        try {
                            result = tempMethod.invoke(controllerAdvice, e);
                        } catch (IllegalAccessException ex) {
                            ex.printStackTrace();
                        } catch (InvocationTargetException ex) {
                            ex.printStackTrace();
                        }
                    }else{
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    }
                }
            }
            //转json
            ObjectMapper mapper = new ObjectMapper();
            //返回字符串
            String json = mapper.writeValueAsString(result);
            response.setContentType("application/json");
            response.getOutputStream().write(json.getBytes("utf-8"));
        } else if (method.equals("OPTIONS")) {
            this.doOptions(request, response);
        } else if (method.equals("TRACE")) {
            this.doTrace(request, response);
        } else if (method.equals("HEAD")) {
            this.doHead(request, response);
        } else {
            //抛出异常
            response.sendError(501, "未知的请求方式！");
        }
    }


    /**
     * 获取请求体
     * @param request
     * @return
     * @throws IOException
     */
    public static byte[] getRequestPostBytes(HttpServletRequest request)
            throws IOException {
        int contentLength = request.getContentLength();
        System.out.println("contentLength ="+contentLength);
        if(contentLength<=0){
            return null;
        }
        byte buffer[] = new byte[contentLength];

        ServletInputStream is = request.getInputStream();
        int len = 0;
        while((len = is.read(buffer,len,contentLength-len))!=-1);
        return buffer;

//        for (int i = 0; i < contentLength;) {
//
//            int readlen = request.getInputStream().read(buffer, i,
//                    contentLength - i);
//            if (readlen == -1) {
//                break;
//            }
//            i += readlen;
//        }
//        return buffer;
    }

    public static String getRequestPostStr(HttpServletRequest request)
            throws IOException {
        byte buffer[] = getRequestPostBytes(request);
        //如果为null，则不需要转格式了
        if(!(buffer!=null&&buffer.length>0)){
            return null;
        }
        String charEncoding = request.getCharacterEncoding();
        if (charEncoding == null) {
            charEncoding = "utf-8";
        }
        return new String(buffer, charEncoding);
    }

}